package cn.sccl.common.snmp;

import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TableEvent;
import org.snmp4j.util.TableUtils;

import java.util.*;

public class SnmpUtils {

    private String community = "public";
    private int retries = 2;
    private int timeout = 1500;
    private int snmpVersion = SnmpConstants.version2c;

    private CommunityTarget communityTarget = null;
    private Snmp snmp = null;

    public SnmpUtils() {

    }

    public String getCommunity() {
        return community;
    }

    public void setCommunity(String community) {
        this.community = community;
    }

    public int getRetries() {
        return retries;
    }

    public void setRetries(int retries) {
        this.retries = retries;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public int getSnmpVersion() {
        return snmpVersion;
    }

    public void setSnmpVersion(int snmpVersion) {
        this.snmpVersion = snmpVersion;
    }

    public CommunityTarget getCommunityTarget() {
        return communityTarget;
    }

    public void setCommunityTarget(CommunityTarget communityTarget) {
        this.communityTarget = communityTarget;
    }

    public Snmp getSnmp() {
        return snmp;
    }

    public void setSnmp(Snmp snmp) {
        this.snmp = snmp;
    }

    public SnmpUtils(String addressStr) throws Exception {
        this.communityTarget = this.initCommunityTarget(addressStr);
        this.snmp = this.initSnmp();
    }

    public CommunityTarget initCommunityTarget(String addressStr) {
        Address address = GenericAddress.parse(addressStr);
        CommunityTarget target = new CommunityTarget();
        // 地址
        target.setAddress(address);
        // 共同体
        target.setCommunity(new OctetString(this.community));
        // 重试次数
        target.setRetries(this.retries);
        // 超时时间
        target.setTimeout(this.timeout);
        // 设置版本
        target.setVersion(this.snmpVersion);
        return target;
    }

    public Snmp initSnmp() throws Exception {
        Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
        snmp.listen();
        return snmp;
    }

    public List<TableEvent> snmpWalk2(OID[] oids) throws Exception {
        TableUtils tableUtils = new TableUtils(this.snmp, new DefaultPDUFactory(PDU.GETBULK));
        // tableUtils.setMaxNumRowsPerPDU(10);
        List<TableEvent> tableEvents = tableUtils.getTable(this.communityTarget, oids, null, null);
        return tableEvents;
    }

    public Map<String,String> snmpWalk(OID[] oids) throws Exception {
        TableUtils tableUtils = new TableUtils(this.snmp, new DefaultPDUFactory(PDU.GETBULK));
        // tableUtils.setMaxNumRowsPerPDU(10);

        // If not null, all returned rows have an index in a range (lowerBoundIndex, upperBoundIndex]
        List<TableEvent> tableEvents = tableUtils.getTable(this.communityTarget, oids, null, null);

        Map<String, String> resultMap = new HashMap<>();
        for (TableEvent item: tableEvents) {
            OID oidIndex = item.getIndex();

            int[] oidIntArr = oidIndex.getValue();
            String keyStr = "";
            for (int i = 0; i < oidIntArr.length; i++) {
                keyStr += oidIntArr[i] + ".";
            }
            keyStr = keyStr.substring(0,keyStr.length()-1);
            String valueStr = "";
            VariableBinding[] variableBindings = item.getColumns();

            if (variableBindings.length == 1) {
                Variable variable = variableBindings[0].getVariable();
                valueStr = variable.toString();
            }
            resultMap.put(keyStr, valueStr);
        }
        return resultMap;
    }

    public PDU sendRequest(String oid, int pduTye) throws  Exception {
        ResponseEvent responseEvent = this.snmp.send(this.createPdu(oid, pduTye), this.communityTarget);
        PDU response = responseEvent.getResponse();
        if (response.getErrorStatus() == PDU.noError) {
            return response;
        }
        return null;
    }

    public void sendAsyncRequest(String oid, int pduType, PDU returnPdu) throws Exception {
        this.snmp.send(this.createPdu(oid, pduType), this.communityTarget, null, new ResponseListener() {
            @Override
            public void onResponse(ResponseEvent responseEvent) {
                PDU response = responseEvent.getResponse();
                System.out.println("Got response from " + responseEvent.getPeerAddress());
                if (response == null) {
                    System.out.println("TimeOut...");
                } else {
                    if (response.getErrorStatus() == PDU.noError) {
                        Vector<? extends VariableBinding> vbs = response.getVariableBindings();
                        for (VariableBinding vb : vbs) {
                            System.out.println(vb + " ," + vb.getVariable().getSyntaxString());
                        }
                    } else {
                        System.out.println("Error:" + response.getErrorStatusText());
                    }
                }
            }
        });
    }

    public PDU createPdu(String oid, int pduType) {
        PDU pdu = new PDU();
        pdu.setType(pduType);
        pdu.add(new VariableBinding(new OID(oid)));
        return pdu;
    }
}
